#pragma once
#include<iostream>
#include<string>
#include<unistd.h>
#include"Mutex.hpp"
#include<fstream>//c++11文件流操作
#include<sstream>//字符串流操作
#include<memory>
#include<filesystem>//C++17
#include"Time.hpp"
//实现的日志形式：
//[时间] [日志等级] [进程id] [文件名] [行号] - 可变内容信息
//代码形式：LOG[DEBUG/INFO/...] <<"HELLO ..." << int << char <<...
//构成：1.构建日志字符串（路径）2.刷新落盘（screen/file）
namespace LogModule
{
    using namespace MutexModule;
    //1.日志默认的路径和文件名
    const std::string defaultpath = "./log/";
    const std::string defaulname = "log.txt";

    //2.日志等级
    enum class LogLevel
    {
        DEBUG = 1,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };
    //获取日志等级信息
    std::string Level2string(LogLevel level)
    {
        switch(level)
        {
            case LogLevel::DEBUG:
            return "DEBUG";
            case LogLevel::INFO:
            return "INFO";
            case LogLevel::WARNING:
            return "WARNING";
            case LogLevel::ERROR:
            return "ERROR";
            case LogLevel::FATAL:
            return "FATAL";
            default:
            return "NONE";
        }
    }

    //3.刷新策略（策略模式）顶级策略
    class LogStrategy
    {
    public:
        //虚函数的使用让基类给派生类只提供接口，实现方式由子类实现，以此来达到多态的效果（防止二义性问题出现）
        virtual ~LogStrategy() = default;
        virtual void SyncLog(const std::string &message) = 0;    //=0表示无函数体
    };


    //3.1.控制台策略（screen）
    class ConsoleLogStrategy:public LogStrategy
    {
    public:
        ConsoleLogStrategy(){}
        ~ConsoleLogStrategy(){}
        void SyncLog(const std::string &message)
        {
            LockGuard lockgurd(_lock);
            std::cout << message << std::endl;
        }

    private:
        Mutex _lock;
    };

    //3.2.磁盘文件策略
    class FileLogStrategy:public LogStrategy
    {
    public:
        // FileLogStrategy()
        // :_logpath(defaultpath)
        // ,_logname(defaulname)
        // {
        //     LockGuard lockguard(_lock);
        //     // //确认文件路径存在
        //     if(std::filesystem::exists(_logpath))
        //     {
        //         return;
        //     }
        //     //不存在则创建
        //     try
        //     {
        //         std::filesystem::create_directories(_logpath);
        //     }
        //     catch(std::filesystem::filesystem_error &e)
        //     {
        //         std::cerr << e.what() << "\n";
        //     }
        // }
        FileLogStrategy(const std::string &logpath  = defaultpath,const std::string &logname = defaulname)
        :_logpath(logpath)
        ,_logname(logname)
        {
            LockGuard lockguard(_lock);
            //确认文件路径存在
            if(std::filesystem::exists(_logpath))
            {
                return;
            }
            //不存在则创建
            try
            {
                std::filesystem::create_directories(_logpath);
            }
            catch(std::filesystem::filesystem_error &e)
            {
                std::cerr << e.what() << "\n";
            }
        }
        ~FileLogStrategy(){}
        void SyncLog(const std::string &message)
        {
            LockGuard lockguard(_lock);
            //打开文件并刷新信息
            std::string log = _logpath + _logname;//"./log/log.txt"
            std::ofstream out(log,std::ios::app);//追加方式打开
            if(!out.is_open()) return;
            // try
            // {
            //    out.is_open();
            // }
            // catch(std::ofstream::failure &e)
            // {
            //     std::cerr << e.what() << "\n";
            // }
            //以流的形式写入
            out << message << "\n";
            out.close();
        }
        
    private:
        std::string _logpath;
        std::string _logname;
        Mutex _lock;

    };

    //日志类（类内类）用于代表一条完整日志信息,根据策略进行刷新
    class Logger
    {
    public:
        Logger()
        {
            //默认采用ConsoleLogStrategy刷新策略
            _strategy = std::make_shared<ConsoleLogStrategy>();
        }
        void EnableConsole()//打开控制台策略
        {
            _strategy = std::make_shared<ConsoleLogStrategy>();

        }
        void EnableFile()//打开文件策略
        {
            _strategy = std::make_shared<FileLogStrategy>();

        }
        ~Logger()
        {}
        //构建一个内部类表示完整信息
        //[时间] [日志等级] [进程id] [文件名] [行号] - 可变内容信息
        class LogMessage
        {
        public:
            LogMessage(LogLevel level,const std::string &filename,const int line,Logger &logger)
            :_currtime(GetCurrTime())
            ,_level(level)
            ,_pid(getpid())
            ,_filename(filename)
            ,_line(line)
            ,_logger(logger)
            {
                std::stringstream ss;
                ss << "[" << _currtime << "] "
                   << "[" << Level2string(_level) << "] "
                   << "[" << _pid << "] "
                   << "[" << _filename << "] "
                   << "[" << _line << "] - ";

                   _loginfo = ss.str();
            }
           //重载<<添加可变部分
           template<typename T>                //LOG(DEBUG)<<"hello"<<a<<b<<...;
           LogMessage &operator<<(const T&info)
           {
                std::stringstream ss;
                ss << info;
                _loginfo += ss.str();
                return *this;
           }
            ~LogMessage()
            {
                if(_logger._strategy)
                _logger._strategy->SyncLog(_loginfo);
            }

        private:
            std::string _currtime;     //日志时间
            LogLevel _level;          //日志等级
            pid_t _pid;              //进程id
            std::string _filename;  //文件名
            int _line;             //行号
            std::string _loginfo; //表示完整信息
            Logger &_logger;     //负责刷新策略的选择
        };

        //重载()实现用LOG(DEBUG)代表[时间] [日志等级] [进程id] [文件名] [行号]
        //！！！！！//就是用拷贝形成临时对象（不用引用返回），临时对象自动析构调用析构函数执行刷新动作！！！
        LogMessage operator()(LogLevel level,const std::string &filename,const int line)
        {
            return LogMessage(level,filename,line,*this);
        }
    private:
        std::shared_ptr<LogStrategy> _strategy;//虚函数不能定义对象，但可以定义指针。定义一个顶级策略的智能指针
    };

    Logger logger;
    //宏定义进行替换，自动获得执行的函数和行号
    #define LOG(Level) logger(Level,__FILE__,__LINE__)
    //宏替换决定刷新策略
    #define ENABLE_CONSOLE() logger.EnableConsole()
    #define Enable_File() logger.EnableFile()

}